#include "netlib/net/tcp_server.h"

#include <cassert>

#include "netlib/base/logger.h"
#include "netlib/net/event/event_loop.h"
#include "netlib/net/socket/sigpipe_ignore.h"
#include "netlib/net/socket/tcp_connection.h"

using namespace std::placeholders;

namespace netlib::net {

void DefaultMessageCallback(const TcpConnectionPtr& conn, Buffer* buffer, Timestamp receiveTime) {
	LOG_INFO << "Receive message: " << buffer->PeekString(buffer->ReadableBytes());
	buffer->RetrieveAll();
}

void TcpServer::DefaultConnectionCallback(const TcpConnectionPtr& conn) {
	LOG_INFO << GetName() << ": " << conn->Name() << " [" << conn->LocalAddress().ToIpPort()
	         << "]<->[" << conn->PeerAddress().ToIpPort() << "] is "
	         << (conn->Connected() ? "UP" : "DOWN");
}

} // namespace netlib::net

netlib::net::TcpServer::TcpServer(EventLoop* loop,
                                  const InetAddress& listen_addr,
                                  bool is_epoll,
                                  std::string&& name,
                                  int n_event_thread) :
    kName(name),
    loop_(loop), acceptor_(loop, listen_addr),
    event_thread_pool_(loop, is_epoll, n_event_thread, name) {
	acceptor_.SetNewConnectionCallback(
	    [this](int connfd, const InetAddress& peer_addr) { NewConnection(connfd, peer_addr); });
}

void netlib::net::TcpServer::Start() {
	LOG_INFO << GetName() << " start!";
	assert(!acceptor_.Listening());
	event_thread_pool_.Start(thread_init_cb_);
	loop_->RunInLoop([object_ptr = &acceptor_] { object_ptr->Listen(); });
}

void netlib::net::TcpServer::NewConnection(int connfd, const InetAddress& peer_addr) {
	static int conn_idx = 0;
	loop_->AssertInLoopThread();
	std::string conn_name(kName + std::to_string(conn_idx++));
	InetAddress local_addr = sockets::GetLocalAddress(connfd);
	TcpConnectionPtr conn(std::make_shared<TcpConnection>(event_thread_pool_.GetNextLoop(), connfd,
	                                                      local_addr, peer_addr, conn_name));
	connections_.emplace(conn_name, conn);
	conn->SetConnectionCallback(conn_cb_);
	conn->SetMessageCallback(msg_cb_);
	conn->SetCloseCallback([this](const TcpConnectionPtr& conn) { RemoveConnection(conn); });
	conn->Established();
}

void netlib::net::TcpServer::RemoveConnection(const TcpConnectionPtr& conn) {
	loop_->RunInLoop([this, conn] { RemoveConnectionInLoop(conn); });
}

void netlib::net::TcpServer::RemoveConnectionInLoop(const TcpConnectionPtr& conn) {
	loop_->AssertInLoopThread();
	LOG_INFO << "remove connection " << conn->Name();
	connections_.erase(conn->Name());
	conn->GetEventLoop()->QueueInLoop([conn] { conn->Destroyed(); });
}
